package com.ruoyi.common.utils.uuid;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;

import java.util.concurrent.atomic.AtomicLong;

/**
 * @ClassDescription:
 * @JdkVersion: 17
 * @Author: yh
 * @Created: 2025-01-04 22:47
 */
@Component
public class SnUtil{
    private static final Logger log = LoggerFactory.getLogger(SnUtil.class);

    private static volatile SnUtil instance;

    // 起始时间戳 (2023-01-01 00:00:00)
    private final long twepoch = 1672444800000L;

    // 时间戳的位数
    private final long timestampBits = 31L;

    // 序列号的位数
    private final long sequenceBits = 32L;

    // 最大时间戳值
    private final long maxTimestamp = ~(-1L << timestampBits);

    // 序列号掩码
    private final long sequenceMask = ~(-1L << sequenceBits);

    // 上次生成ID的时间戳
    private long lastTimestamp = -1L;

    // 序列号
    private long sequence = 0L;

    // 线程安全的锁对象
    private final Object lock = new Object();

    private SnUtil(){}

    // 公有静态方法，返回单例实例
    public static SnUtil getInstance() {
        if (instance == null) { // 第一次检查，避免不必要的同步
            synchronized (SnUtil.class) { // 同步块，防止多线程同时进入创建多个实例
                if (instance == null) { // 第二次检查，确保实例尚未创建
                    instance = new SnUtil(); // 创建实例
                }
            }
        }
        return instance; // 返回实例
    }

    public String generateId(){
        return String.valueOf(nextId());
    }

    // 生成唯一ID
    public long nextId() {
        synchronized (lock) {
            long timestamp = timeGen();

            // 如果当前时间小于上次生成ID的时间戳，抛出异常
            if (timestamp < lastTimestamp) {
                throw new RuntimeException(
                        String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
            }

            // 如果在相同的毫秒内生成ID，则递增序列号
            if (lastTimestamp == timestamp) {
                sequence = (sequence + 1) & sequenceMask;
                // 如果序列号溢出，则阻塞直到下一毫秒
                if (sequence == 0) {
                    timestamp = tilNextMillis(lastTimestamp);
                }
            } else {
                // 如果是新的毫秒，重置序列号
                sequence = 0L;
            }

            lastTimestamp = timestamp;

            // 生成ID
            return ((timestamp - twepoch) << sequenceBits) | sequence;
        }
    }

    // 阻塞直到下一毫秒
    protected long tilNextMillis(long lastTimestamp) {
        long timestamp = timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = timeGen();
        }
        return timestamp;
    }

    // 获取当前时间戳（毫秒）
    protected long timeGen() {
        return System.currentTimeMillis();
    }



}
